perm filename MCMAN4.S78[206,LSP] blob
sn#356961 filedate 1978-05-18 generic text, type T, neo UTF8
**DRAFT** The Compiler **DRAFT**
Part 4 - The Compiler
Table of Contents
1. Peculiarities of the Compiler . . . . . . . . . . . . . . . . . . . 4-1
1.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-1
1.2 In-line (or Open) Coding . . . . . . . . . . . . . . . . . . . . . . 4-3
1.3 Function Calling . . . . . . . . . . . . . . . . . . . . . . . . . . 4-5
1.4 Input to the Compiler . . . . . . . . . . . . . . . . . . . . . . . 4-7
1.5 Output of the Compiler . . . . . . . . . . . . . . . . . . . . . . . 4-8
1.6 Functions Connected with the Compiler . . . . . . . . . . . . . . .4-10
2. Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-11
3. Running Compiled Functions . . . . . . . . . . . . . . . . . . . . .4-17
4. Running the Compiler . . . . . . . . . . . . . . . . . . . . . . . .4-19
5. The Lisp Assembly Program, LAP . . . . . . . . . . . . . . . . . . .4-25
5.1 LAP on the pdp-10 . . . . . . . . . . . . . . . . . . . . . . . . .4-25
5.1.1 The LAP Function . . . . . . . . . . . . . . . . . . . . . . . . . .4-25
5.1.2 Valid LAP Code Forms . . . . . . . . . . . . . . . . . . . . . . . .4-27
5.1.3 LAP Syllables . . . . . . . . . . . . . . . . . . . . . . . . . . .4-31
5.1.4 Differences Between lap and faslap . . . . . . . . . . . . . . . . .4-35
5.2 LAP on Multics . . . . . . . . . . . . . . . . . . . . . . . . . . .4-36
5.2.1 LAP Words . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-36
5.2.2 LAP Instructions . . . . . . . . . . . . . . . . . . . . . . . . . .4-37
5.2.3 LAP Operands . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-39
5.2.4 LAP Expressions . . . . . . . . . . . . . . . . . . . . . . . . . .4-40
5.2.5 Using LAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-41
6. Calling Programs Written in Other Languages . . . . . . . . . . . .4-43
6.1 The defpl1 declaration . . . . . . . . . . . . . . . . . . . . . . .4-43
6.2 Producing fasloadable files with the Midas Assembler . . . . . . . 4-48
February 7, 1977 Page 4-1
**DRAFT** **DRAFT**
1. Peculiarities of the Compiler
LISP programs can be compiled into machine code. This representation of a
program is more compact than the interpreted list-structure representation, and
it can be executed much more quickly. However, a price must be paid for these
benefits. It is not as easy to intervene in the execution of compiled programs
as it is with interpreted programs. Thus most LISP programs should not be
compiled until after they have been debugged.
In addition, not all LISP programs can be compiled. There are certain things
which can be done with the interpreter that cannot be effectively compiled.
These include indiscriminate use of the functions eval and apply, especially
with pdl-pointer arguments; "nonlocal" use of the go and return functions;
functions which modify themselves. Also there are a number of functions which
detect illegal arguments when they are called interpretively but not when a call
to them is compiled; therefore erroneous compiled programs can damage the LISP
environment and can cause strange errors to occur - be forewarned. However,
most "normal" programs are compilable.
Some operations are compiled in such a way that they will behave somewhat
differently than they did when they were interpreted. It is sometimes necessary
to make a "declaration" in order to obtain the desired behavior. This is
explained on page 4-11.
1.1 Variables
In the interpreter "variables" are implemented as atomic symbols which
possess shallow-bound value cells. The continual manipulation of value cells
would decrease the efficiency of compiled code, so the compiler defines two
types of variables: "special variables" and "local variables." Special
variables are identical to variables in the interpreter.
Local variables are more like the variables in commonly-used algebraic
programming languages such as Algol or PL/I. A local variable has no associated
atomic symbol; thus it can only be referred to from the function that possesses
it. The compiler creates local variables for prog-variables, do-variables, and
lambda-variables, unless directed otherwise. The compiled code stores local
variables in machine registers or in locations within a stack.
February 7, 1977 Page 4-1
**DRAFT** Maclisp Reference Manual **DRAFT**
The principal difference between local variables and special variables is in
the way a binding of a variable is compiled. (A binding has to be compiled when
a prog-, do-, or lambda-expression is compiled, and for the entry to a function
which has lambda-variables to be bound to its arguments.) If the variable to be
bound has been declared to be special, the binding is compiled as code to
imitate the way the interpreter binds variables: the value of the atomic symbol
is saved and a new value is stored into its value cell. If the variable to be
bound has not been declared special, the binding is compiled as the declaration
of a new local variable. Storage is assigned and code is generated to store the
value to which the variable is to be bound into the register or stack-location
assigned to the new local variable. This runs considerably faster than a
special binding.
Although a special variable is associated with an atomic symbol which is the
name of the variable, the name of a local variable appears only in the input
file; in compiled code there is no connection between local variables and atomic
symbols. Because this is so, a local variable in one function may not be used
as a "free variable" in another function since there is no way for the location
of the variable to be communicated between the two functions.
When the usage of a variable in a program to be compiled does not conform to
this rule, i.e. it is somewhere used as a "free variable," the variable must be
declared special. The necessary special declaration of such a variable provides
a convenient place to put a comment explaining and and defending its non-local
usage. There are two common cases in which this occurs. One is where a
"global" variable is being used, i.e. a variable which is setq'ed by many
functions but is never bound. The other is where two functions cooperate, one
binding a variable and then calling the other one which uses that variable as a
free variable.
Page 4-2 ∪4-1.1 February 7, 1977
**DRAFT** Peculiarities of the Compiler **DRAFT**
1.2 In-line (or Open) Coding
Another difference between the compiler and the interpreter is "in-line
coding," also called "open coding." When a form such as (and (foo x) (bar)) is
evaluated by the interpreter, the built-in function and is called to perform the
desired operation. But to compile this form as a call to the function and with
list-structured arguments derived from (foo x) and (bar) would negate much of
the advantage of compiling. Instead the compiler recognizes and as part of the
LISP language, then compiles machine code to carry out the intent of (and (foo
x) (bar)) without actually calling the and function. This code might look like:
pick up value of variable x
call function foo
is the result nil?
if yes, the value of the and is nil
if no, call the function bar
the result of the and is what bar returned.
This "in-line coding" is done for all special forms (cond, prog, and, errset,
setq, etc.); thus compiled code will usually not call any of the built-in
fsubrs.
Another difference between the compiler and the interpreter has to do with
arithmetic operations. Most computers on which Maclisp is implemented have
special instructions for performing all the common arithmetic operations. The
Maclisp compiler contains a "number compiler" feature which allows the LISP
arithmetic functions to be "in-line coded" using these instructions.
A problem arises here because of the generality of the Maclisp arithmetic
functions, such as plus, which are equally at home with fixnums, flonums, and
bignums. Most present-day computers are not this versatile in their arithmetic
instructions, which would preclude open-coding of plus. There are two ways out
of this problem: one is to use the special purpose functions which only work
with one kind of number. For example, if you are using plus but actually you
are only working with fixnums, use + instead. The compiler can compile (+ a b
c) to use the machine's fixnum-addition instruction. The second solution is to
write a form such as (plus a b (foo c)), but tell the compiler that the values
of the variables a and b and the result of the function foo can never be
anything but fixnums. This is done by means of the "number declarations" which
are described on page 4-11.
February 7, 1977 ∪4-1.2 Page 4-3
**DRAFT** Maclisp Reference Manual **DRAFT**
Another problem that can arise in connection with the in-line coding of
arithmetic operations is that the LISP representation of numbers and the machine
representation of numbers may not be the same. Of course, this depends on the
particular implementation. If these two representations are different, the
compiler would store variables which were local and declared to be numeric-only
in the machine form rather than the LISP form. This could result in compilation
of poor code which frequently converts number representations and in various
other problems. Compilers which have this problem provide a (closed t)
declaration which inhibits open coding of arithmetic operations.
Page 4-4 ∪4-1.2 February 7, 1977
**DRAFT** Peculiarities of the Compiler **DRAFT**
1.3 Function Calling
Another property of compiled code that should be understood is the way
functions are called. In the interpreter function calling consists of searching
the property list of the called function for a functional property (if it is an
atomic symbol) and then recursively evaluating the body of the function if it is
an expr, or transferring control to the function if it is a subr. In compiled
code function calling is designed according to the belief that most of the
functions called by compiled code will be machine executable, i.e. "subrs":
other compiled functions, or built-in functions, and only infrequently will
compiled code call an interpreted function. Therefore a calling mechanism is
used which provides for efficient transfer between machine-executable functions
without constant searching of property lists. This mechanism is called the "uuo
link" mechanism for historical reasons.
When a compiled function is first loaded into the environment, it has a uuo
link for each function it will call. This uuo link contains information
proclaiming that it is "unsnapped" and giving the name of the function to be
called, which is an atomic symbol. The first time a call is made through such a
uuo link, the fact that it is "unsnapped" is recognized and a special linking
routine is entered. This routine searches the property list of the function to
be called, looking for a functional property (or an autoload property) in just
the same way as the interpreter would. If the function turns out to be an expr,
or is undefined, the interpreter is used to apply the function and the result is
given back to the compiled code. The link is left "unsnapped" so that every
time this function is called the interpreter will be invoked to interpret its
definition. If, however, the function being called is machine executable (a
subr), the link is "snapped." Exactly what this means is implementation
dependent but the effect is that from now on whenever a call is made through
this uuo link control will be transferred directly to the called function using
the subroutine-calling instruction of the machine, and neither the linking
routine nor the interpreter will be called.
There is a flag which can be set so that links will not be snapped even if
they go to a function which is machine executable. This flag is the value of
the atomic symbol nouuo. (See part 3.5.) There is also a function, (sstatus
uuolinks), which unsnaps all the links in the environment. These facilities are
used in circumstances such as when a compiled function is redefined, or compiled
code is being traced or otherwise debugged.
In the pdp-10 implementation a uuo link is implemented as an instruction
February 7, 1977 ∪4-1.3 Page 4-5
**DRAFT** Maclisp Reference Manual **DRAFT**
which is executed when a call is to be made through the link. An "unsnapped"
link consists of a special instruction, "UUO", which causes the LISP linking
routine in the interpreter to be called. The address field of the uuo points to
the atomic symbol which names the function to be called. The operation code and
accumulator fields indicate the type of call and number of arguments. When the
link is snapped the UUO instruction is replaced with a "PUSHJ" instruction,
which is the machine instruction for calling subroutines.
In the Multics implementation, a uuo link is implemented as a pointer. To
call through this link a "tspbp" instruction indirect through the pointer is
used. An unsnapped link points at the linking subroutine and various fields in
the pointer, left unused by the machine, indicate the type of call, number of
arguments, and the atomic symbol which names the function. When the link is
snapped the pointer is changed to point at the first instruction of the called
function.
Before a function can be used it must be made known in the LISP environment.
Interpreted functions are made known simply by putting a functional property on
the property list of the atomic symbol which names the function. This is
usually done using the built-in function defun. Compiled functions must be made
known by a more complex mechanism known as "loading," because of the complexity
of the support mechanisms needed to make compiled functions execute efficiently.
In some dialects of LISP the compiler automatically makes the compiled functions
known, but in Maclisp the compiler creates a file in the file system of the host
operating system, and this file has to be loaded before the compiled function
can be called. In the pdp-10 implementation this file is called a "fasl file."
In the Multics implementation it is called an "object segment." Loading is
described in detail on page 4-17.
Page 4-6 ∪4-1.3 February 7, 1977
**DRAFT** Peculiarities of the Compiler **DRAFT**
1.4 Input to the Compiler
The input to the compiler consists of an ascii file containing a number of S-
expressions. The format of this file is such that it could be read into a LISP
environment using a function such as load or uread, and then the functions
defined in this file would be executed interpretively.
When a file is compiled, the compiler reads successive S-expressions from the
file and processes them. Each is classified as a function definition, a
declare, or a "random form" according to what type of object it is and according
to its car if it is a list.
A function definition is a form whose car is defun or defprop. If the
function defines a macro, the macro is defined for use at compile time. If it
defines an expr or a fexpr, the compiler translates the definition from LISP to
machine code and outputs it into the "fasl file" or "object segment" which is
the output from the compiler. If it defines some other property, it is treated
as a random form.
A random form is anything read from the input file that is not a function
definition or a declare. It is simply copied into the output file of the
compiler in such a way that when that file is loaded it will be evaluated.
A declare is a form whose car is the atom declare. See for how it is
processed by the compiler. It is ignored by the interpreter because there is an
fsubr called declare in Maclisp which does nothing.
Note that if a form is read from the input file and its car has been defined
as a macro, the compiler will apply the macro and then process the result as if
it had been read from the input file. Thus if foo is a macro which expands (foo
a b c) into (defun a ...), the resulting function definition will be compiled.
February 7, 1977 ∪4-1.4 Page 4-7
**DRAFT** Maclisp Reference Manual **DRAFT**
1.5 Output of the Compiler
The output of the compiler normally consists of error and warning messages on
the terminal, and a file of machine code which can be loaded into a lisp
environment with load or fasload. In the pdp-10 implementation it is also
possible to get a "lap file." This is a file which contains machine code in
symbolic form.
In the Multics implementation the compiler produces a standard object segment
with a translator name of "lisp" and a symbol section which contains the
information used by load to define functions, set up constants needed by the
compiled code, create "uuo links", etc.
When the object segment is "loaded", it is not copied into the lisp
environment. Instead a "linkage block" is set up in the environment and
initialized according to directives in the segment's symbol section. This block
includes the reference name of the object segment and a pointer to it. Thus
compiled code is automatically shared between multiple users in the Multics
implementation. However, list structure constants used by the compiled code can
never be shared.
In the pdp-10 implementation the output of the compiler is a "fasl file."
This file begins with a header identifying it as a fasl file and indicating what
version of lisp it was produced for. (This is used to detect
incompatibilities.) The rest of the file consists of a series of directives to
load and relocate code words, set up list-structure constants, reference value
cells of symbols, evaluate random S-expressions, etc.
fasload operates by reading through the file, storing code in lisp's binary
program space, and generating the necessary LISP objects for constants used by
the compiled code. Normally none of this is shared between users, but see part
6.5 for information on how to make it pure and shared.
There is a function defined in the compiler, coutput, which can be used to
put a random S-expression into the output file. When the file is loaded, this
S-expression will be evaluated. This can be used to print the version number of
the program, initialize its data base, etc. It cannot be used to fool around
with obarrays because of the way the loader handles atomic symbols: For
efficiency, it builds a table of all the atoms needed by the file being loaded,
and creates and interns them all just once. This makes loading much faster, but
means that everything in one file has to go on the same obarray.
Page 4-8 ∪4-1.5 February 7, 1977
**DRAFT** Peculiarities of the Compiler **DRAFT**
The coutput function usually does not have to be used, since the compiler
coutputs any form it reads from the input file that does not look like a
declaration or a function definition. It is provided for the benefit of certain
hairy macros.
February 7, 1977 ∪4-1.5 Page 4-9
**DRAFT** Maclisp Reference Manual **DRAFT**
1.6 Functions Connected with the Compiler
declare FSUBR
In the interpreter, declare is like comment. In the compiler, the
arguments are evaluated at compile time. This is used to make
declarations, to gobble up input needed only in the interpreter, or to
print messages at compile time. Examples:
(declare (special x y) (*fexpr f00))
(declare (read)) ;in compiler, gobble next S-expression.
(something-needed-only-in-the-interpreter)
(declare ((lambda (↑w) (princ "Now compiling fubar"))
nil))
%include FSUBR
(%include name) is used to cause an "include file" to be included in the
input to the compiler. It works in the interpreter also, causing the
specified file to be inpush'ed. name may be a string or an atomic symbol.
The translator search rules are used.
Note: this function presently exists only in the Multics implementation.
See also the nouuo switch, part 3.5.
Page 4-10 ∪4-1.6 February 7, 1977
**DRAFT** Declarations **DRAFT**
2. Declarations
It is often necessary to supply information to the compiler in order to
compile a function beyond the definition of the function with defun, which is
all that the interpreter needs in order to interpret the function. This
information can be supplied through declares.
A declare is a list whose first element is the atom declare and whose
remaining elements are forms called "declarations." The compiler processes a
declare by evaluating each of the declarations, at compile time. Usually the
declarations call on one of the declaration functions which the compiler
provides. These are described below. However, it is permissible for a
declaration to be any evaluable form, and it is permissible for a declaration to
read from the input file by using the read function. This may be used to
prevent the compiler from seeing certain portions of the input which are only
needed when a program is run interpretively. Prefixing a form in the input file
with (declare (eval (read))) would cause it to be evaluated at compile time if
the file was compiled or at read-in time if the file was interpreted.
Arbitrarily complex compile-time processing may be achieved by the combination
of declarations and macros.
The remainder of this section describes the declaration functions provided by
the compiler. Note that if a declaration function described below is of the
form (foo t), its effect can be reversed by using the form (foo nil).
(special var1 var2 ... )
Declares var1, var2, etc. to be special variables.
(unspecial var1 var2 ... )
Declares var1, var2, etc. to be local variables.
(*expr fcn1 fcn2 ... )
Declares that fcn1, fcn2, etc. are expr- or subr-type functions that will
be called. This declaration is generally supplied by default by the
compiler but in some peculiar circumstances it is required to tell the
compiler what is going on when the same symbol is used as both a function
and a variable. It is good practice to put *expr, *lexpr, and *fexpr
declarations for all the functions defined in a file near the beginning of
that file.
February 7, 1977 ∪4-2. Page 4-11
**DRAFT** Maclisp Reference Manual **DRAFT**
(*lexpr fcn1 fcn2 ... )
Declares fcn1, fcn2, etc. to be lexpr- or lsubr-type functions that will be
called. This declaration is required for non-builtin functions unless the
functions are defined in the file being compiled and are not referenced by
any functions that are defined before they are.
(*fexpr fcn1 fcn2 ... )
Declares fcn1, fcn2, etc. to be fexpr- or fsubr-type functions that will be
called. This declaration is required for non-builtin functions unless the
functions are defined in the file being compiled and are not referenced by
any functions that are defined before they are.
(**array arr1 arr2 ... )
Declares arr1, arr2, etc. to be arrays that will be referred to. See the
note under *expr.
(fixnum var1 var2 ... )
Declares var1, var2, etc. to be variables whose values will always be
fixnums.
(fixnum (fcn type1 type2 ... ) ... )
Declares fcn to be a function which always returns a fixnum result. Also
the types of the arguments may be declared as type1, type2, etc. An
argument type may be fixnum, meaning the argument must be a fixnum, flonum,
meaning the argument must be a flonum, or notype, meaning the argument may
be of any type.
The two types of fixnum declarations may be intermixed, for example (fixnum
x (f00 fixnum) y).
(flonum var1 var2 ... (fcn type1 ... ) ... )
Is the same as the fixnum declaration except the variables or function-
results are declared to always be flonums.
(notype var1 var2 ... (fcn type1 ... ) ... )
Is the same as the fixnum declaration except the variables or function-
results are declared not to be of any specific type.
(fixsw t)
Causes the compiler to assume that all arithmetic is to be done with
fixnums exclusively, except that obviously functions such as +$ and cos
will still use flonums.
Page 4-12 ∪4-2. February 7, 1977
**DRAFT** Declarations **DRAFT**
(fixsw nil)
Turns off the above.
(flosw t)
Causes the compiler to assume that all arithmetic is to be done with
flonums exclusively, except that obviously functions such as + and rot will
still use fixnums.
(flosw nil)
Turns off the above.
fixsw and flosw are variables so (setq fixsw t) is an equivalent
declaration to (fixsw t).
(setq special t)
Causes all variables to be special.
(setq nfunvars t)
Causes the compiler to disallow functional variables. All symbols in
function position in a form are assumed to have a functional property at
run time. The case of a symbol whose value is a functional form is
disallowed.
(macros t)
Causes macro definitions to be retained at run time.
(macros nil)
Causes macros to be defined only at compile time. This is the default
case.
(genprefix foo)
Causes auxiliary functions generated by the compiler (for instance when
function is used) to be named foon, where n is a number incremented by 1
each time such a function is generated. The genprefix declaration is used
when several separately compiled files are to be loaded together, in order
to avoid name clashes. A reasonable convention would be to use the name of
a file as the genprefix within that file. If you don't give a genprefix
declaration, the compiler uses !g or something like that.
February 7, 1977 ∪4-2. Page 4-13
**DRAFT** Maclisp Reference Manual **DRAFT**
(array* (type arr1 n1 arr2 n2 ... ) ... )
Is used to declare arrays arr1, arr2, etc. type may be fixnum, flonum, or
notype; it indicates what type of objects will be contained in the arrays.
n1, n2, etc. are the number of dimensions in arr1, arr2, etc.
respectively. The extended form (array* (type (arr1 dim1.1 dim1.2 ...
dim1.n) ...)) can be used. It is preferred if the dimensions are known at
compile-time. The dimensions declared must be either fixnums or nil or ?,
which indicate a dimension not known at compile time. If dimensions are
declared, the compiler can generate faster code.
The array* declaration causes the compiler to generate in-line code for
accesses of and store's into the arrays declared. This code is somewhat
faster than the usual subroutine-call array accessing. The compiler will
also generate in-line code if the arraycall function is used; in this case
the array must be named by an array-pointer rather than by an atomic
symbol.
(arith (type1 fcn1 fcn2 ...) (type2 fcn21 fcn22 ... ) ... )
Is used to declare a general arithmetic function such as plus to be
replaced by a one-type arithmetic function such as +. fcn1, fcn2, etc. are
the functions to be replaced. type1, etc. is the type of function to
replace them with: fixnum means replace them with the corresponding fixnum-
only functions, e.g. replace plus by +. flonum means replace them with the
corresponding flonum-only functions, e.g. replace plus by +$. notype means
turn off a previous arith declaration for these functions.
The following declarations are useful only in the pdp-10 implementation;
however, the Multics implementation will accept them and ignore those which are
irrelevant.
(mapex t)
In the pdp-10 implementation, causes all map-type functions to be open-
coded as do loops. (This is always done in the Multics implementation.)
The resulting code is somewhat larger than otherwise, but also somewhat
faster.
(mapex nil)
Causes map-type functions to actually be called. This is the default.
Page 4-14 ∪4-2. February 7, 1977
**DRAFT** Declarations **DRAFT**
(noargs t)
Causes the compiler not to output information as to the number of arguments
each function compiled takes; this provides some saving of memory space in
" non-Bibop pdp-10 implementations.
(noargs nil)
Causes the compiler to output number of arguments information. This is the
default.
(messioc chars)
Causes an (ioc chars) to be done just before printing out each error
message. In this way one may direct error messages to the LAP file instead
of to the terminal on the pdp-10.
The default messioc is vr which puts the messages in both places.
(muzzled t)
Prevents the pdp-10 fast-arithmetic compiler from printing out a message
every time closed compilation of arithmetic is forced.
(muzzled nil)
Causes the compiler to print a message when closed-compilation is forced.
This is the default.
(symbols t)
Causes the compiler to output LAP directives so that the LAP assembler will
attempt to pass assembly symbols to DDT for debugging purposes.
(symbols nil)
Does not generate debugging symbols. This is the default.
(closed t)
Causes arithmetic operations to be close-compiled, that is, the function +
will generate in-line code but the function plus will not in any
circumstances. This declaration is necessary if you apply plus to two
fixnums and want a bignum result if the operation overflows.
(closed nil)
Causes the compiler to produce code that assumes overflow will not occur,
which may give incorrect results in the above case. When the compiler can
determine, by declaration or implication, that all of the operands to an
arithmetic function are fixnums (or flonums), it will generate code to use
the hardware fixnum (or flonum) instructions. This is the default state.
February 7, 1977 ∪4-2. Page 4-15
**DRAFT** Maclisp Reference Manual **DRAFT**
This declaration only exists in the Multics implementation.
(defpl1 ...)
Defines an interfacing function which may be used to call programs written
in other languages, such as PL/I. See part 4.6 for details.
Page 4-16 ∪4-2. February 7, 1977
**DRAFT** Running Compiled Functions **DRAFT**
3. Running Compiled Functions
After a file of functions has been compiled, those functions can be loaded
into an environment and then used. They can be loaded either by using the load
or fasload functions described below, or by using the autoload feature described
on part 3.4.
The following function is at present available only in the Multics
implementation.
load SUBR 1 arg
(load x), where x is a file specification acceptable by openi, i.e. a
namestring or a namelist, causes the specified file to be loaded into the
environment. The file may be either a source file or a compiled file
(called a "fasl" file in the ITS implementation and an object segment in
the Multics implementation.) load determines which type of file it is and
acts accordingly. A source file is loaded by openi'ing and inpush'ing it.
A read-eval loop is then executed until the end of the file is reached. An
object file is loaded by reading it, defining functions as directed by
specifications inserted in the file by the compiler.
fasload FSUBR
fasload takes the same arguments as uread. It causes a file of compiled
functions, called a "fasl" file in some implementations, to be loaded in.
Example:
(fasload foo fasl dsk macsym)
The following function only exists in the Multics implementation.
February 7, 1977 ∪4-3. Page 4-17
**DRAFT** Maclisp Reference Manual **DRAFT**
defsubr LSUBR 3 to 7 args
defsubr is the function used to define new machine code functions. It
defines various types of functions, depending on its arguments. The way to
define a subr written in PL/I is
(defsubr "segname" "entryname" nargs)
which defines segname$entryname as a subr expecting nargs arguments. The
value returned is a pointer which can be putprop'ed under the subr property
or the fsubr property. The way to define an lsubr written in PL/I is
(defsubr "segname" "entryname" nargs2*1000+nargs1 -2)
which defines segname$entryname as an lsubr allowing from nargs1 to nargs2
arguments. The 1000 is octal. The value returned should be putprop'ed
under the lsubr property.
Examples:
(putprop 'mysubr (defsubr "myfuns" "mysubr" 1) 'subr)
(putprop 'myfsubr (defsubr "myfuns" "myfsubr" 0) 'fsubr)
(putprop 'mylsubr
(defsubr "myfuns" "mylsubr" 2001 -2) 'lsubr)
A function defined in this way receives its arguments and returns its value
on the marked pdl, which may be accessed through the external static
pointer
lisp←static←vars←$stack←ptr
See part 6.6 for details on how to access the arguments, and on the
internal format of LISP data. lisp←static←vars←$nil and
lisp←static←vars←$t←atom are fixed bin(71) external static; they contain
nil and t.
Page 4-18 ∪4-3. February 7, 1977
**DRAFT** Running the Compiler **DRAFT**
4. Running the Compiler
in the Multics implementation
The compiler is invoked by the lisp←compiler command to Multics. This
command can be abbreviated lcp. The arguments to the command are the pathname
of the input file and options. The compiler appends ".lisp" to the given
pathname unless it is preceded by the -pathname or -pn option. The output
object segment is created in the working directory with a name which is the
first component of the name of the input file. For example, the command
lcp dir>foo.bar
reads the file "dir>foo.bar.lisp" and produces an object segment named "foo" in
the working directory.
Usually no options need be supplied, since there are defaults. The options
available are:
-pathname -pn -p
Causes the following argument to be taken as the exact pathname of the
input file, even if it begins with a minus sign. ".lisp" will not be
appended.
-eval
Causes the following argument to be evaluated by LISP. For example,
lisp←compiler foo -eval "(special x y z)"
-time -times -tm
As each function is compiled, its name and the time taken to compile it
will be typed out.
-total←time -total -tt
At the end of the compilation, metering information will be typed out.
-nowarn -nw
Suppresses the typing of warning messages. Error messages of a severity
greater than "warning" will still be typed.
February 7, 1977 ∪4-4. Page 4-19
**DRAFT** Maclisp Reference Manual **DRAFT**
-macros -mc
Equivalent to the (macros t) declaration: Causes macro definitions to be
retained at run time.
-all←special
Causes all variables to be made special. Equivalent to the (setq special
t) declaration.
-genprefix -gnp -gp
Takes the following argument as the prefix for names of auxiliary functions
automatically generated by the compiler. Equivalent to the genprefix
declaration.
-check -ck
Causes only the first pass of the compiler to be run. The input file is
checked for errors but no code is generated and no object segment is
produced.
-ioc
If the following argument is x, (ioc x) is evaluated. The main use for
this "-ioc d" which turns on garbage-collection messages during
compilation.
-list -ls
Causes a listing file to be created in the working directory, containing a
copy of the source file and a table of functions defined and referenced.
If the object segment is named "name", the listing file will be named
"name.list".
-long -lg
Causes the listing file to also contain an assembly language listing, with
commentary, of the generated code.
-no←compile -ncp
Causes the compiler not to attempt to compile the file. Instead the input
file is simply treated as being composed entirely of random forms. It is
digested into a form which can be processed quickly by the load function.
in the ITS pdp-10 implementation
The ITS compiler is presently in an anomalous state. There are two versions,
COMPLR and NCOMPLR. NCOMPLR contains the fast-arithmetic facilities described
Page 4-20 ∪4-4. February 7, 1977
**DRAFT** Running the Compiler **DRAFT**
here. COMPLR is an older version which will soon go away. At that time,
NCOMPLR will be renamed to COMPLR. This documentation uses the name COMPLR to
refer to what is now NCOMPLR, so it is presently inaccurate but will become
accurate in the future.
Invoke the compiler with the :COMPLR command. The compiler will announce
itself, print an underscore or backarrow, and accept a command line, which
should be of the standard form
<output file> ← <input file> (switches)
The file specifications should be standard ITS file names, e.g.
DEV:DIRNAM;FNAME1 FNAME2. If it is necessary to get a "funny" character such as
← into the file name, it may be quoted with a slash.
The compiler normally processes a file of LISP functions and produces a so-
called "LAP file", containing S-expressions denoting pdp-10 machine-language
instructions, suitable for use with LAP (the Lisp Assembly Program). However,
one may direct the compiler instead to produce a binary object file, called a
"FASL FILE", suitable for use with the fasload function or the autoload feature.
A third option is to process a previously generated file of LAP code to produce
a FASL file. This is especially useful in the case where special-purpose
functions have been hand-coded in LAP.
If one specifies only an input file name, say FOO BAR, then by default the
name of a generated LAP file will be FOO LAP, and of a FASL file, FOO FASL.
The various modes of operation of the compiler may be controlled by
specifying various switches, which are single letters, inside parentheses at the
end of the command line. A switch may be turned off by preceding the switch
letter with a minus sign. Extraneous or invalid switches are ignored.
Initially all switches are off (the use of minus sign described above is
provided in case the compiler is used for several files in succession).
The most commonly-used switch setting is "(FK)", which causes a FASL file to
be produced.
Most of the switches correspond to values of atomic symbols within the
compiler. These are noted in parentheses.
The switches are:
A (assemble) The specified input file contains LAP code which is to be
made into a binary FASL file.
February 7, 1977 ∪4-4. Page 4-21
**DRAFT** Maclisp Reference Manual **DRAFT**
D Disown. Causes the compiler to disown itself after it has started
running. This is the safest way to disown a COMPLR, because the compiler will
know that it can't try to get any information from DDT.
F (fasl) Accept a file of LISP functions, produce a LAP file, and then
assemble the LAP file into a FASL file. This is probably the most useful mode.
With the K switch the LAP file is not actually produced at all; the lap code is
sent directly to faslap as the compiler generates it.
K (nolap) Kill LAP file. Delete the LAP file after assembly. Usually
used in conjunction with the F switch.
Mo(in)(macros) Equivalent to (declare (macros t)). Causes macro definitions to
be defined at run time as well as at compile time.
N (noargs) No args properties. Equivalent to (declare (noargs t)).
Normally the compiler outputs information in the LAP code as to how many
arguments each function requires, so that args properties may be created on the
appropriate atomic symbols at load time. In some implementations these
properties occupy a significant amount of list space; thus it may be desirable
to eliminate these properties.
S (special) Equivalent to (declare (setq special t)). Causes all
variables to be considered special.
T (ttynotes) Causes the compiler to print a note on the user's terminal
as each function is compiled or assembled. This switch is normally off so that
a COMPLR may be proceeded and allowed to run without the TTY. In any case error
messages will be printed out on the terminal.
Uo(in)(unfaslcomments) Useful only in conjunction with the F or A switch.
Causes the assembler to output comment messages into a file whose second file
name is UNFASL. (Actually, this file is always created, and error comments will
be directed into this file also if messioc so specifies; but the file is
immediately deleted if it contains nothing significant.) These comment messages
describe the size of each function assembled, and give other random information
also.
V (nfunvarsf1) Equivalent to (declare (nfunvars t)); disallows functional
variables.
W (muzzled) (i.e. Whisper). Equivalent to (declare (muzzled t)).
Prevents the fast-arithmetic compiler from printing out a message when closed
compilation of arithmetic is forced.
X (mapex) Equivalent to (declare (mapex t)). Causes all map-type
functions to be open-coded as do loops. The resulting code is somewhat larger,
but also somewhat faster.
Z (symbols) Equivalent to (declare (symbols t)). Causes the compiler to
output a special directive in the LAP code so that the LAP assembler will
attempt to pass assembly symbols to DDT for debugging purposes. Primarily of
use to machine language hackers.
Page 4-22 ∪4-4. February 7, 1977
**DRAFT** Running the Compiler **DRAFT**
COMPLR will accept a "Job Command Line" if desired; simply type
:COMPLR <command line><cr>
In this mode COMPLR will automatically proceed itself and run without the TTY,
and kill itself when done.
It may be desirable to execute some LISP functions in the compiler before
actually compiling a file. Typing ctrl/G will cause the compiler to announce
itself and then type an asterisk; you will then be at lisp's top level. To make
the compiler accept a command line, say (maklap) or type ctrl/↑. One useful
function for debugging and snooping around is cl; (cl foo) will compile the
function foo, which should be defined in the compiler's lisp environment, and
print LAP code onto whatever device(s) are open for output.
February 7, 1977 ∪4-4. Page 4-23
**DRAFT** Maclisp Reference Manual **DRAFT**
Page 4-24 ∪4-4. February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
5. The Lisp Assembly Program, LAP
Maclisp includes a facility by which machine-language programs can be defined
as LISP functions. This can be used to gain direct access to the hardware or
the operating system, and may also be used by the compiler.
The Lisp Assembly Program translates S-expressions which resemble the native
assembly language of the host machine into machine language, and sets things up
so that machine language coding can be called by LISP programs in the same way
that built-in "subrs" are called.
5.1 LAP on the pdp-10
The lisp compiler for the pdp-10 implementation does not output binary object
files directly; rather, it outputs a series of S-expressions denoting the
machine-language instructions of the compiled function. There are two programs
which accept such S-expressions and convert them to binary machine language,
called lap and faslap. (Historical note: the word "lap" dates back to 7090
LISP, and is derived from the phrase "Lisp Assembly Program".) lap is an in-core
assembler; it reads in the S-expressions (hereafter referred to as "lap code")
and deposits the resulting binary instructions in the binary program space of
the current lisp environment. faslap, on the other hand, takes a file of lap
code and produces a binary file suitable for use with fasload. faslap is
normally part of the pdp-10 lisp compiler, but in some implementations with
limited memory it can be a separate program. Both assemblers will accept the
same lap code, except for certain peculiar conditions. This section will
describe the lap function and lap code; differences between lap and faslap will
be treated in a special section.
5.1.1 The LAP Function
lap is an fsubr which is executed primarily for its side effect - loading in
a binary program. It accepts a series of S-expressions similar in form to a
program written in MIDAS or MACRO-10. It is not intended to be a fancy
assembler: it does not have conditional assembly, macros, or complex literal
generation features. It does, however, contain sufficient power to load the
February 7, 1977 ∪4-5. Page 4-25
**DRAFT** Maclisp Reference Manual **DRAFT**
output of the compiler, plus enough extra features that simple machine-language
functions may be hand-coded in it. (See the section on conventions for writing
lap code by h`πt on part 6.6. The major operational differences between lap and
MIDAS or MACRO-10 are that (1) lap is one-pass, while the others take two, (2)
lap uses the function read to input lap code, while the others are more
efficient, and (3) lap assembles directly into the lisp environment, while the
others produce a binary object file (note that faslap differs only in the first
two respects).
The lap function is an fsubr which expects to get two atomic symbols as
arguments; the first is the name of the function to be assembled, and the second
is the type (i.e. the property under which it is to be stored on the property
list.) Thus
(lap quux subr)
would assemble a subr called quux. When invoked, lap repeatedly calls the read
function, operating on the S-expressions thus obtained, until a nil is
encountered, at which time the assembly ends. Some messages may be printed out
as this happens. If the assembly completed successfully, the variable bporg is
updated to reflect the new size of binary program space, and the appropriate
property is placed on the property list of the specified atomic symbol.
Normally, lap does not reside in the initial lisp system, though the initial
system does contain several specialized functions for use by lap. Instead, lap
has an autoload property of (lap fasl com) on ITS and (lap fasl sys) on the DEC-
10 system. Thus, if one simply reads in a file of lap code lap will load
automatically and assemble the functions.
Here is an example of some lap code which corresponds roughly to the lisp
function memq:
(LAP FUNNY-MEMQ SUBR)
(ARGS FUNNY-MEMQ (NIL . 2))
MEMBEG (JUMPE B MEMEND) ;result nil if arg 2 nil
(HLRZ T 0 B) ;else look at car of arg 2
(CAIN T 0 A)
(JRST 0 MEMEND) ;win if same as arg 1
(HRRZ B 0 B) ;else take cdr of arg 2
(JRST 0 MEMBEG) ; and try again
MEMEND (MOVEI A 0 B) ;return arg 2
(POPJ P) ;exit from function
NIL
Page 4-26 ∪4-5.1.1 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
Note that this is greatly different in style from 7090 lap, which took the
entire program as one argument, and a symbol table as the other. The drawback
with the 7090 method is that the entire program must be read in before it is
assembled; this can require prohibitively large amounts of memory, especially
for the lap output from compilation of a large lisp function. The method used
by pdp-10 lap is much more reasonable in practical situations (e.g. reading in
lap code from a file).
lap does not use an a-list for its symbol table, either. Rather, the value
of the symbol is stored on the property list under the sym property. Thus
(defprop ztesch 43 sym) would make the symbol ztesch known to lap, with the
value 43. lap has a number of symbols initially defined, including the names of
all the accumulators, and the addresses of some useful routines internal to
lisp. It also uses the function getmidasop on a symbol if the symbol is
otherwise undefined to determine whether it is a pdp-10 instruction (the
getmidasop function contains a concise table of all pdp-10 instructions and most
monitor calls, as well as names of UUO's used internally by lisp). In this way
lap can recognize all standard instruction mnemonics without defining 400 or
more sym properties. If lisp's symbol table has been loaded into DDT, then lap
will ask DDT about the values of symbols as a last resort. In this manner hand-
coded lap code may refer to any location internal to lisp (with an appropriate
amount of caution, of course).
When lap terminates, it returns as its value a list of the new value of bporg
and the entry point(s) of the function defined (hand-coded functions may have
more than one entry point). If any symbols were undefined or multiply defined,
they will be printed out first. It is generally a good idea to let lap
terminate naturally, rather than quitting out of it, since it hacks the lisp
environment in various peculiar ways.
5.1.2 Valid LAP Code Forms
lap acts on the S-expressions it reads as follows:
nil
Terminate assembly and return. Any literals generated are assembled into
memory at the end of the function, temporary symbol definitions are
flushed, and (gctwa t) is evaluated. The nil should be followed by a
space, because carriage return is not always an atom separator. In
particular, the compiler depends on there being a space there.
February 7, 1977 ∪4-5.1.1 Page 4-27
**DRAFT** Maclisp Reference Manual **DRAFT**
atomic symbol
Assign to the atomic symbol (non-nil, of course) a temporary sym property
equal to the address of the next word to be assembled into. Thus one uses
atomic symbols as location tags. If (symbols t) is in effect lap will pass
the value of this symbol to DDT if lisp's symbol table has been loaded.
(defsym atom1 value1 ... atomn valuen)
For each i define atomi with a sym property of (eval valuei). No binary
words are generated, and these symbols are not passed to DDT. Note that
this performs a lisp evaluation, not a lap evaluation!
(entry name type) or (entry name)
Defines the atomic symbol name to be a function of type type with entry
point at the current location. If type is not specified, it defaults to
the second argument to lap, i.e. the type of the principal entry to this
function. No binary words are generated. This form is not used by the
output of the lisp compiler, and is provided only as an aid to writers of
hand-coded lap code. It allows several functions to share symbols and
storage areas.
(args atom args-prop)
If the assembly terminates successfully, then atom gets args-prop as its
args property. atom must be an entry point of the function being
assembled. No binary words are generated. The pdp-10 lisp compiler will
output this declaration in each function compiled unless (declare (noargs
t)) or the N switch is given. faslap requires that each args declaration
follow the corresponding entry point.
(comment ...)
This form is totally ignored. Of course, since the lisp reader is used to
input lap code, semicolon comments may be used as well.
Page 4-28 ∪4-5.1.2 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
(eval form1 ... formn)
Applies the function eval to each form in turn. No binary words are
generated. This is useful for such things as (eval (setq ibase 23.)) or
whatever.
(symbols t-or-nil)
Controls the lap feature which tries to pass symbolic location names to
DDT. Furthermore, if the symbols pseudo-operation occurs anywhere within a
given lap function, the names and locations of the entry points of that
function will also be passed to DDT. No binary words are generated. Note
that there is possibility for confusion here because lap will accept as
tags atomic symbols with names of any length, while MIDAS and DDT truncate
tags to six characters. Thus quuxbar and quuxbaz are two different symbols
to lap, but will interfere with each other when passed to DDT. When
symbols are passed to DDT, the names are truncated to six characters, and
any non-squoze character (a character other than A-Z, 0-9, ., $, or %) is
assumed to be a dot. Since dots must be slashified to be read into lisp,
the standard convention is to use * (the lisp compiler uses this
convention). Thus one would write (JSP D *LCALL) instead of (JSP D
/.LCALL).
(ddtsyms atom1 atom2 ... atomn)
Ordinarily lap asks ddt for the values of any symbols which are undefined.
If this declaration appears, lap will get the values of atom1 through atomn
from ddt, and any other undefined symbols will be considered as errors.
(block fixnum)
Assembles a block fixnum words long, containing zeros.
(ascii S-expression)
explodec's the S-expression and assembles the characters obtained into
successive words, five per word, in ASCII code.
February 7, 1977 ∪4-5.1.2 Page 4-29
**DRAFT** Maclisp Reference Manual **DRAFT**
(sixbit S-expression)
Similar to ascii, but characters are assembled six per word in sixbit code
(ascii characters between 40 and 137 are represented as 0 to 77).
(squoze atom) or (squoze fixnum atom)
Produces a word of squoze code, which is a left-justified radix-50 code,
from the first six characters of the print name of atom. If fixnum is
present, then it is divided by 4 and the low four bits of the quoutient are
added into the high four bits of the squoze value (the MIDAS convention).
The squoze word generated is in ITS format, which is different from DEC
format.
(begin temp-syms-list form)
The form is evaluated (as a lisp form, not a lap expression). The result
should be a list of valid LAP code forms, which will be assembled one at a
time. They may use symbols which appear in the temp-syms-list. These
symbols will have a local scope confined to the begin, i.e. their sym
properties will be saved and restored. This provides a primitive macro and
conditional assembly facility.
any other list
Assembles a single word, which is assumed to be an instruction of some
kind. First, if the list contains the atomic symbol @, it is deleted from
the list and saved. Then the first four components of the list are
processed in order. These must all be lap "syllables" (described below).
If the length of the list is less then four, missing elements are assumed
to be zero. If the length of the list is more than four, the extra
elements are ignored. The four elements of the list are assumed to be, in
order, the operation code, accumulator, address, and index fields of a pdp-
10 instruction. These are evaluated by the lap syllable evaluator to
obtain four numbers, which are then added together after being modified as
follows:
Page 4-30 ∪4-5.1.2 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
opcode no change
accumulator shift left 23. places
address clear left half
index swap halves
Finally, if the atom @ had been present, the octal number 20000000 is
logically or'ed into the result, thus turning on the indirection bit. Note
that neither the accumulator nor the index field is truncated to four bits.
This has many useful applications; see, for example, the description of the
specbind routine below.
There is a fairly strong similarity between code written in lap and
equivalent code written in MIDAS or MACRO-10. The essential difference is that
lap processes assembly fields in order from left to right in order to determine
which field is which. One pitfall to avoid is writing such instructions as
(JRST FOO) or (SETZM FOO) when one intends rather (JRST 0 FOO) or (SETZM 0 FOO).
Another difference to remember is that lap uses the lisp reader to input lap
code; thus one must remember to put spaces around an @, and that one cannot
write (JRST 0 FOO+3) unless FOO+3 really is a tag! (For arithmetic operations
within assembly fields, see the description of lap syllables below.) If it is
desired to make lap code look more like the standard assembly languages, one may
use the fact that comma is like a space to lisp, and that extra parentheses
don't hurt, and write (MOVE A,TABLE(D)) instead of (MOVE A TABLE D). Be sure to
remember that the index field is in the left half because it is the fourth
component, not because it is in parentheses.
5.1.3 LAP Syllables
Each of the four components of assembly words are evaluated by the lap
evaluator to produce numeric quantities; these are then combined to form an
assembly word. Note that @ is treated specially and is not a component. Forms
to be evaluated by the lap evaluator are called lap syllables. Valid forms for
lap syllables are as follows:
a number
Fixnums evaluate to themselves, and may be operated upon by lap
arithmetic operations. Flonums also evaluate to themselves, but arithmetic
February 7, 1977 ∪4-5.1.2 Page 4-31
**DRAFT** Maclisp Reference Manual **DRAFT**
operations on them will not work. Flonums should not be used in the
address field, because the left halves will be truncated off; they should
be used only in the opcode or index fields (the latter is useful for
writing (FADRI 7 0 3.0) or something like that).
nil
same as (quote nil).
*
Evaluates to the address of the word into which the current instruction
will be assembled. Equivalent to . in MIDAS and MACRO-10 (however, see the
note below about literals).
an atomic symbol
Any atomic symbol other than @, *, and nil evaluates to its assembly
symbol value. That is, if the symbol has a sym property, then it is the
value of that property; otherwise, the value returned by the getmidasop
function if non-nil; otherwise, the value which DDT assigns to it. An
error occurs if no value can be found for a symbol.
(quote S-expression)
Protects S-expression from garbage collection, and evaluates to the
address of the S-expression. Thus (MOVEI A '(A B)) puts the address of the
S-expression (A B) into accumulator A. Warning: faslap permits this
syllable only in the address field.
(function S-expression)
Same as (quote2 S-expression), but emphasizes that S-expression is a
function. Thus one might write (CALL 2 (FUNCTION CONS)).
Page 4-32 ∪4-5.1.3 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
(special atom)
Evaluates to the address of the value cell of the atomic symbol atom.
If atom does not have a value cell, one is created for it first. Thus, for
example,
(MOVE A (SPECIAL QUUX))
(MOVEM A (SPECIAL ZTESCH))
Accomplishes the equivalent of (SETQ ZTESCH QUUX). faslap permits this
syllable only in the address field.
(array atom)
Evaluates to the address of the TTSAR of the array which is on the
property list of atom. If atom is not yet an array, a dummy array property
is created. This is of use for open-coded array accessing. The TTSAR
contains a pointer to the array which has an index field of TT. The
garbage collector knows to alter this pointer whenever the array is
relocated. Typically one operates on an array by putting the appropriate
index in TT and then indirecting through the TTSAR.
(ascii S-expression)
Evaluates to a 36-bit quantity consisting of the ascii representation of
the first five explodec'd characters of S-expression. Note that the ascii
pseudo-op may generate several binary words as a lap form, but only a
single-word quantity as a syllable.
(sixbit S-expression)
Like ascii, but uses the first six characters and produces a sixbit
representation quantity.
(squoze atom) or (squoze fixnum atom)
(ITS only) Similar to the same form as a lap form: produces a word of
squoze code as its value.
February 7, 1977 ∪4-5.1.3 Page 4-33
**DRAFT** Maclisp Reference Manual **DRAFT**
(+ lapsyl1 lapsyl2 ... lapsyln)
Adds together the values of the lap syllables lapsyl1 through lapsyln.
(Thus note that lap syllables are defined recursively.) This allows one to
write such things as (JRST 0 (+ FOO 3)).
(- lapsyl)
Evaluates to the negative of the value of lapsyl.
(- lapsyl1 lapsyl2 ... lapsyln)
Subtracts the values of the lap syllables lapsyl2 through lapsyln from
the value of the lap syllable lapsyl1.
(lapsyl1 lapsyl2 ... lapsyln)
Same as (+ lapsyl1 lapsyl2 ... lapsyln).
(lapsyl)
Evaluates to the value of lapsyl. It most definitely does not evaluate
to the swapped-halves value of lapsyl, as some might think! When one
writes (MOVE A,FOO(B)), the value of B gets swapped because it is in the
index field, and not because it is in parentheses.
(% lap assembly word)
Generates a literal; i.e. the lap assembly word is saved and assembled
at the end of the function. The value of the syllable is the address of
this remotely generated word. lap assembly word must be an instruction, or
one of the ascii, sixbit, or block pseudo-ops. (The block pseudo-op is
relatively useless here.) Thus, for example, (MOVEI T (% SIXBIT LONG-
MESSAGE!)) is perfectly valid. Note: * in a literal refers to the
location of the literal, not of the referencing instruction. Thus (JUMPE A
(% AOJA T (+ * 1))) will not do what you might expect from using MIDAS.
Finally, faslap permits literals only in the address field.
Page 4-34 ∪4-5.1.3 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
5.1.4 Differences Between lap and faslap
Much effort has been made to keep lap and faslap compatible. There are of
necessity, however, some differences. faslap reads the lap code at assembly
time, and not at load time, which means that read macro characters and obarray
hackery may not happen at the right time. faslap and fasload cooperate in a
scheme to gain speed by calling the function intern only once on each atomic
symbol needed by a file of functions; faslap creates a table of such symbols and
passes them when encountered into the binary file. This means that switching
obarrays in the middle of a fasload file will lose.
There are also some internal differences due to the different modes of
operation. As an in-core assembler, lap does not need to worry about questions
relating to relocatability. faslap, however, does not know where in memory a
binary file will be loaded, and thus must produce relocatable binary code. This
implies that faslap must distinguish between relocatable and absolute symbols.
This is done by using non-numeric sym properties for relocatable symbols; the
user who hand-codes lap code and expects to look at sym properties at assembly
time should be aware of this.
faslap furthermore does not know into what version of lisp the binary file
will be loaded. This poses a problem because compiled code needs to refer to
routines and locations internal to lisp, such as FLOAT1 and ERSETUP. This is
solved by the so-called globalsym convention; these labels, which for lap have
numeric sym properties, in faslap have non-numeric sym properties, and direct
faslap to output directions to fasload to find the correct value of a symbol for
the lisp being loaded into. For most purposes such symbols should be treated as
a funny kind of relocatable symbol.
faslap imposes some restrictions on the use of certain constructs. Multiple
and negative relocatability is not permitted. Relocatable symbols, the quote,
function, special, and sar0 constructs, and literals are permitted only in the
address field of an instruction.
February 7, 1977 ∪4-5.1.4 Page 4-35
**DRAFT** Maclisp Reference Manual **DRAFT**
5.2 LAP on Multics
A LAP program begins with the form
(lap fn type nargs)
This defines the function fn, which is of type type (subr, lsubr, or fsubr.)
nargs is the number of arguments expected by the function. In the case of an
lsubr, this is nine bits of the maximum number of arguments followed by nine
bits of the minimum number of arguments.
Following this form is a series of "LAP words," terminated by nil.
5.2.1 LAP Words
A LAP program consists of a sequence of LAP words, or statements. Usually a
LAP word generates one word of object code, but some LAP words are pseudo-ops
which generate no code, and some lap words generate many words of code.
The allowed formats for LAP words are as follows:
A number. This generates a word whose contents is that number. Octal numbers
which LISP would normally treat as bignums because the high order bit is
on but the number is not negative, such as 400000710120, are handled
properly. A flonum is also allowed, and a word containing the machine
representation of that flonum will be generated.
An atomic symbol. As in prog, this defines a label or tag at the current
location.
(entry fn type nargs). This defines an additional entry point. The arguments
are the same as in the lap header line.
(comment ...) is ignored.
(eval form1 form2 ...) evaluates the forms (as lisp forms, not lap expressions)
but does not do anything with the results.
(defsym sym1 val1 sym2 val2 ...). This defines values for symbols. The values
are evaluated as LISP forms, not as LAP expressions.
Page 4-36 ∪4-5.2 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
(equ sym1 val1 sym2 val2 ...). This is similar to defsym except that the values
are evaluated as LAP expressions. (See page 4-40 for the details of LAP
expressions.)
(block n) generates n words of zeroes. It is unclear how useful this is, since
the code generated by LAP goes into a read-only object segment.
(ascii some text) explodec's the text and generates a string of the
corresponding ascii characters. This is not a LISP string, just the
characters themselves. If the number of characters is not a multiple of
four, the last word is filled out with zeroes (null characters).
(bind symbol value) generates a binding word for use with the binding operator.
See page 4-42.
(get-linkage) loads the lb register with a pointer to the Multics linkage
section. The external operand (refer to page 4-40) may be used to refer
to external data and procedures once the lb is loaded. lb is used instead
of lp because LISP uses lp internally.
A list whose car is a LISP macro will be expanded. This provides LAP with the
primitive makings of a macro facility. The result of the macro should be
a list of LAP words, or nil.
Anything else will be assembled as an instruction. The next section describes
the format of instructions.
5.2.2 LAP Instructions
Instructions have essentially the same format as in the ALM assembler, with
the following exceptions: Since LAP words are lists, instructions are enclosed
in parentheses. Comments must be introduced by semicolon. Index-register tags
must be in the form "x7" rather than just "7." This is because in LAP tag fields
are general expressions and are not evaluated specially. By default numbers are
octal, but a trailing point indicates decimal (as in LISP.) Arithmetic
expressions are written differently. (See page 4-40.) The rpt, rpd, and rpl
instructions are not supported. The format of literals is different from that
used by ALM. The ALM pseudo-ops, particularly vfd, are not present, but could
be simulated using macros. ALM's format for external references is not used.
The use of spaces and commas is freer than in ALM. Vertical bar may be used
freely since in the LAP reader it is a single character object.
February 7, 1977 ∪4-5.2.1 Page 4-37
**DRAFT** Maclisp Reference Manual **DRAFT**
The allowed formats for ordinary instructions are:
(opcode)
(opcode operand)
(opcode operand tag)
(opcode pointer|operand)
(opcode pointer|operand tag)
For instructions such as "epp" which need a register operand:
(opcode register operand)
(opcode register operand tag)
(opcode register pointer|operand)
(opcode register pointer|operand tag)
Note that LAP treats comma and space identically, and use of commas in the above
formats can make them more like ALM. Also, ALM lacks an opcode for spri in the
second format above, because the symbol spri is already used for a different
instruction. In LAP, use sprip.
EIS instructions and descriptors are written in the same format as with ALM,
e.g.
(mlr (pr,rl),(pr,x6),fill(040))
(desc4ls bp|-1(3),46,3)
The various fields are all general LAP expressions, except that the words pr,
id, and rl are special-cased.
The tag field in an instruction may be any tag known to the machine. It may
also be the special value $. The following are equivalent:
(tnz frob,$)
(tnz (- frob *),ic)
or in ALM, tnz frob-*,ic
The pointer register names which appear in opcodes, in register fields in the
second format of instructions, and in "pointer|" fields may be chosen from among
Page 4-38 ∪4-5.2.2 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
0, 1, 2, 3, 4, 5, 6, 7
ap, ab, bp, bb, lp, lb, sp, sb
ms, op, tp, cp, lp, rp, sp, sb, us
us is a pseudo pointer register which points at the unmarked stack. It actually
consists of a combination of ab and x7, and therefore cannot be used in EIS
instructions. See part 6.6 for the standard usage of the pointer registers.
The operand field in an instruction may take on a number of forms, which are
described in the next section.
5.2.3 LAP Operands
A LAP instruction may have either an ordinary ALM-type operand, a literal, or
a special lispish operand. In general, the latter do not allow tags.
The allowed operand formats are:
A number, a symbol, or any LAP expression. This is an ordinary ALM-type
operand. The symbol * represents the current location, as in ALM.
(% code) or (%% code). These operands are literals. The contained code is
assembled at the end of the program and the instruction refers to that
address. If %% is used, the literal is placed on a double-word boundary.
The code may be either a single LAP word or several; since tags are
disallowed in literals, if the first item in a literal is an atomic
symbol, then the literal is taken to be a single word. Otherwise it is a
list of words. Note that inside a literal the value of * is the location
of the instruction that referenced the literal, not the location of the
literal. Examples:
February 7, 1977 ∪4-5.2.2 Page 4-39
**DRAFT** Maclisp Reference Manual **DRAFT**
(ana (% 000777777777))
(eraq (%% -2 777777000000))
(eppbp (% ascii Now is the time))
(tnz (% (eax1 1,x1)
(tze frob)
(tra (+ * 1)) )) ;return to loc after tnz
(quote S-expression) refers to a LISP constant.
(special var) refers to the value cell of a special variable (an atomic symbol).
(array name type ndims) refers to an array. This is intended to be used with
the xec instruction for in-line accessing of arrays. See part 6.6.
(function name type nargs) refers to a LISP (or LAP) function. It is intended
to be used with the call (tspbp) instruction. If type is lsubr, an "eax5
-2*nargs" instruction should precede the call. One calls a function by
pushing the arguments onto the ap stack, then doing the call instruction
indirect through the function-link created by the function operand. Upon
return, the arguments have been popped and the result is in the aq.
(function ap|n type nargs) refers to a computed function, located in a cell in
the stack. This is used instead of simply tspbp ap|-n,* so that the
interpreter can get invoked if it is not a compiled function.
(external "seg$ent") refers to an external item. No tag or offset may be used.
Use the (get-linkage) pseudo-instruction to make the external item
addressable.
5.2.4 LAP Expressions
LAP expressions are used as operands of instructions, tag fields, EIS length
fields, and in general wherever a numeric value is needed. The allowed formats
are:
A symbol. Somewhere the symbol must be defined, by use of defsym, equ, a tag,
or the symbol may be one whose definition is built into LAP.
Page 4-40 ∪4-5.2.3 February 7, 1977
**DRAFT** The Lisp Assembly Program, LAP **DRAFT**
* has the value of the current location.
A number. This has the value of the machine representation of that number.
(+ lap-expr1 lap-expr2 ...) is the sum of the values of the lap-expressions.
The + may be omitted. If the list is empty, the value is zero.
(- lap-expr1 lap-expr2 ...) subtracts the values of the expressions lap-expr2,
..., lap-exprn from the value of the expression lap-expr1. However,
(- lap-expr) is the negative of the value of the expression lap-expr.
(symb arg1 arg2 ...), where symb is a LISP macro, expands the macro and uses the
result as an operand.
5.2.5 Using LAP
The LAP assembler may be used in either of two ways: as a translator which
is invoked from Multics command level to assemble a file of LAP programs and
produce an object segment which can be loaded into lisp; or as a lisp function
which will read a lap program from the current input source and assemble it into
the lisp environment. In the pdp-10 implementation these are called "faslap"
and "lap" respectively, but in the Multics implementation they are the same
thing. If lisp tries to evaluate a form such as (lap foobar subr 2), then lap
will be automatically loaded into the environment and it will read in and
assemble until nil is encountered. This mode should be used with caution since
loading lap defines a lot of functions which might conflict with names already
in use.
The more common way of using lap is as a Multics command:
lap name -options-
reads the file name.lap and produces an object segment called name in the
working directory. This segment may then be loaded into the lisp environment
with the load function. This is similar to the operation of the lisp←compiler
command. [[[ ??? WHAT ARE THE OPTIONS ??? ]]]
LAP reads forms from the source file and processes them as follows:
February 7, 1977 ∪4-5.2.4 Page 4-41
**DRAFT** Maclisp Reference Manual **DRAFT**
(lap function type nargs) introduces a LAP program. The assembler reads and
assembles until nil, then returns to this scan.
(declare ...) is the same as in the compiler. It can be used to cause things to
happen at compile time.
(%include name) causes an include file name.incl.lap to be read in the same way
as the main file.
Macro definitions (with defun or defprop) are evaluated as they are seen.
A form whose car is a macro is expanded and re-processed.
[[[[[[[[ ********** Need to discuss: operators available to lap.
+ other internal cruft. how to use macros.
Page 4-42 ∪4-5.2.5 February 7, 1977
**DRAFT** Calling Programs Written in Other Languages **DRAFT**
6. Calling Programs Written in Other Languages
6.1 The defpl1 declaration
The Multics lisp compiler provides a feature by which you can compile a lisp
subr which represents, in the lisp environment, a subroutine in the outside
world which has a PL/I-compatible calling sequence. The Multics Fortran, PL/I,
and Basic compilers use this calling sequence. The BCPL compiler uses it for
"main" routines. Most Multics system entries can be called from lisp through
defpl1.
When the lisp subr is applied, the subroutine will be called with arguments
derived from the arguments given to the lisp subr. Results returned by the
subroutine may be passed back to lisp either as the return value of the lisp
subr, or by setq'ing an atomic symbol.
Because lisp and PL/I use different data types, a correspondence between the
types must be set up:
Numbers. `fixed binary' with a precision not more than 35. corresponds to
the lisp fixnum. `float binary' with a precision of not more than 27.
corresponds to the lisp flonum. Nonzero scale factors, complex numbers, decimal
or pictured numbers, and large precisions are not supported.
Bit strings. A bit string of up to 36. bits corresponds to a lisp fixnum.
The bits are stored left-justified in the fixnum; thus in the case of bit(1) the
fixnum is zero for "0"b and negative for "1"b. Note that because of the left-
justification many bit strings map into "illegal" fixnums which cannot be typed
in as octal numbers. Typing in the corresponding digits would produce a bignum.
The lsh function or the "←" number-modifier character can be useful for
inputting these fixnums. These bit strings work as either `aligned' or
`unaligned.' Bit strings longer than 36. bits are not supported.
Character strings. Lisp character strings and PL/I character strings
correspond directly. For input arguments, lisp will also automatically convert
an atomic symbol to a character string by taking its pname, as usual. Usually
the PL/I argument will be declared `char(*).'
February 7, 1977 ∪4-6. Page 4-43
**DRAFT** Maclisp Reference Manual **DRAFT**
Varying Character Strings. Varying character strings are somewhat special.
Lisp will take whatever string argument you supplied (the null string if it is a
`return' argument) and create a varying string of the length you declared,
initialized with the string you supplied. Thus usually its current length will
be less than its maximum length. This varying string will be passed to the PL/I
subroutine. When the subroutine returns, whatever it leaves in the varying
string will be made back into a lisp string and returned (if it is an `update'
or `return' argument.) This procedure is necessary because lisp strings may not
vary in length. Note that you must declare the length of the string to lisp;
`char(*) varying' is illegal. However, the PL/I subroutine may declare it
`char(*) varying' since a descriptor is passed.
Pointers. Both packed and unpacked pointers are supported. These are both
represented in lisp as fixnums in packed pointer format, that is 2 octal digits
of bit offset, 4 octal digits of segment number, and 6 octal digits of word
offset. The null pointer is 007777000001 octal. It is not possible to
reference, within lisp, what a pointer points at. Because of the packed pointer
representation, ring numbers in pointers are not supported. If you declare the
PL/I subroutine to take unpacked pointers, which is the default, lisp will do
the conversion between packed and unpacked representations.
Raw lisp objects. A PL/I subroutine which knows about lisp may be passed (or
return) raw lisp objects. In PL/I these should be declared `fixed bin(71)' and
then the based overlays declared in sundry lisp include files should be used.
See section 14.6.
Arrays. Arrays of any number of dimensions may be passed. The arrays can
only contain numbers or raw lisp objects however. Usually you would pass a lisp
fixnum (or flonum) array and in PL/I declare it `dimension(*,*) fixed bin(35)'
(or float bin(27).) In the dimension attribute put as many stars as there are
dimensions. Proper matching of types and dimensions will be checked at run
time.
There are certain pitfalls associated with arrays. Arrays with more than 15
dimensions may tend to lose, due to the format of PL/I array argument
descriptors. Arrays as return or update arguments (defined below) are not
supported. However, the lisp array is passed by reference, so if the PL/I
subroutine stores into elements of the array the appropriate thing will happen.
If you are calling a Fortran program, you need to be aware that Fortran reverses
the order of the subscripts of multidimensional arrays.
Because lisp passes arguments by value, while PL/I passes arguments by
Page 4-44 ∪4-6.1 February 7, 1977
**DRAFT** Calling Programs Written in Other Languages **DRAFT**
reference, it is necessary to pay attention to whether an argument is input to
the PL/I subroutine, output from (returned by) the PL/I subroutine, or both
(updated by the PL/I subroutine.) `Output from' includes both arguments that
are stored into and values returned by a return statement. If the PL/I
subroutine has a `returns' attribute, this is considered to be an extra argument
stuck on the end of the argument list. Note that PL/I `returns(char(*))',
`returns(dimension(*) fixed bin)', and similar constructs are not supported
because they use a non-standard calling sequence.
Input arguments to the PL/I subroutine are derived from arguments to the lisp
subr which represents it according to the data type transformations described
above.
Return arguments from the PL/I subroutine are passed back to lisp according
to the user's declaration; they may be ignored, setq'ed onto an atomic symbol,
or passed back as the value of the lisp subr. If more than one is passed back
in the latter way, they are consed up into a list. If there are none, nil is
returned.
Update arguments are a combination of the two types described above. They
are derived from the arguments to the lisp subr, and they are also passed back
like return arguments.
Now the detailed syntax of the `defpl1' feature will be described. It is
invoked by using the defpl1 declaration in the lisp compiler, in a form
generally as follows (note that nothing in this "form" is evaluated):
(declare (defpl1 lisp-name external-name arg-dcl-1
arg-dcl-2 ... arg-dcl-n ))
lisp-name is an atomic symbol, which will be defined as a subr when the output
of the compilation is loaded. This subr will take as many arguments as the PL/I
subroutine has input and update arguments.
external-name is a string which is the name of the subroutine to be called,
as it would be written in PL/I. If it is "", the pname of lisp-name will be
used so that you need not type the same thing twice.
arg-dcl-1 through arg-dcl-n are lists. Each one gives the attributes of one
of the arguments to the PL/I subroutine. First you must give attributes
describing whether it is an input, update, or return argument. These are:
<no attribute given>an input argument
February 7, 1977 ∪4-6.1 Page 4-45
**DRAFT** Maclisp Reference Manual **DRAFT**
return a return argument, passed back as the value of the
subr.
return ignore a return argument which is ignored.
return (setq var) a return argument to which the atomic symbol var is
setq'ed. var should be declared special.
update an update argument, passed back as the value of the
subr.
update ignore an update argument whose returned value is ignored.
update (setq var) an update argument whose returned value var is setq'ed
to.
Next you specify the data type attributes, in a form quite similar to the way
you would in PL/I. (But don't forget that the declaration of each argument is
enclosed in its own pair of parentheses, instead of being separated from the
others with commas.) The following keywords are recognized for data type
attributes:
fixed float binary bin
bit pointer ptr packed-ptr
packed-pointer character char aligned
unaligned lisp array varying
Note that `packed-pointer' is used rather than `pointer unaligned,' and
`array' is used rather than `dimension.' `lisp' means a raw lisp object.
Precisions, array extents, and string lengths are specified as parenthesized
numbers or asterisks, just as in PL/I. Note that unless you declare otherwise
to the compiler or put a decimal point, these numbers will be interpreted as
octal.
Here is an example, although not of a very useful case:
(declare (defpl1 hcs←$initiate "" (char(*)) (char(*)) (char(*))
(fixed bin(1)) (fixed bin(2)) (return pointer)
(return (setq code) fixed bin(35.))))
If this was compiled and loaded into lisp, you could type
(hcs←$initiate ">system←control←1" "whotab" "" 0 0)
Page 4-46 ∪4-6.1 February 7, 1977
**DRAFT** Calling Programs Written in Other Languages **DRAFT**
and lisp would reply with a number such as 356000000, and code would have
been setq'ed to 0, presumably. The "whotab" could then be accessed via an
external array (see page 2-85).
It is important to note that the defpl1 declaration is not known to the
interpreter. A defpl1-defined function may be called by interpreted lisp code,
but the source of the defpl1 declaration must nevertheless be compiled and
loaded into the lisp environment before it can be used. For this reason, it is
a good idea to keep defpl1's and defun's in separate files. The defpl1's may be
placed in an include file which is %include'd by the other file when it is
compiled, and may also be compiled separately when the interpreter is to be
used.
February 7, 1977 ∪4-6.1 Page 4-47
**DRAFT** Maclisp Reference Manual **DRAFT**
6.2 Producing fasloadable files with the Midas Assembler
Midas can assemble FASL files that can be loaded by LISP in the same manner
as compiler output. This mode is entered by the .FASL pseudo-op, which must
appear at the beginning of the file before any storage words.
After .FASL has been seen, the assembly becomes a two pass relocatable
assembly. However, certain restrictions and "changes of interpretation" apply.
Global symbols (declared as usual with " or .GLOBAL) are permissible.
However, since the output is to be loaded with fasload using DDT's symbol table
instead of STINK, there are quite a few differences in detail.
For symbols defined within the current assembly, the only effect of being
declared GLOBAL is that the GLOBAL information is passed on to fasload when the
symbol table is written at the end of pass 2. This in combination with the
symbols switch in fasload determines whether the symbol gets loaded into DDT's
symbol table. If symbols is nil, no symbols will be loaded; if the value of
symbols is the atomic symbol symbols, only globals will be loaded; and if
symbols is t, all symbols (local and global) will be loaded. Once the symbol is
loaded (or not), the information as to its GLOBALness is lost and, of course,
makes no further difference. The initial state when LISP is loaded is nil.
GLOBAL symbols not defined in the current assembly are also legal, but there
are additional restrictions as to where in a storage word they may appear and
what masking may be specified (as compared to a normal relocatable assembly).
Briefly, they may appear in a storage word as a full word, a right half, a left
half, or an accumulator. They may be negated, but can not be operated on with
any other operator. Error printouts will be produced if they appear elsewhere.
When the symbol is encountered by fasload, DDT's symbol table is consulted. If
it is not defined at that time, fasload will try to find a sym property on the
atomic symbol with the same name.
Any sort of global parameter assignment or location assignment is forbidden.
.LOP, .LVAL1, .LVAL2, etc are not available.
The following pseudo-ops are available to facilitate the communication
between MIDAS assembled programs and LISP (particularly with regard to list
structure).
.ENTRY function type args
Page 4-48 ∪4-6.2 February 7, 1977
**DRAFT** Calling Programs Written in Other Languages **DRAFT**
Note that the arguments to this pseudo-op are separated by spaces, not
commas.
function is an atom and is taken as the name of a function beginning at
the current location. type should be one of SUBR, FSUBR, or LSUBR, and has
the obvious interpretation. args is a numeric-valued field which is passed
through to fasload and used to construct the args property of the function.
If it is zero, no args property is created. Otherwise it is considered to
be a halfword divided into two 9-bit bytes, each of which is converted as
follows:
byte result
0 nil
777 777
otherwise n n-1
These two items are then cons'ed and form the args property.
The following pseudo-ops may appear in constants.
.ATOM atom
Followed by a LISP atom in "MIDAS" format (see below). May only appear
in right half (or entire word) of a storage word. Assembles into a pointer
to the atom.
.SPECIAL atom
Similar to .ATOM but assembles into a pointer to the (special) value
cell of the specified atom.
.FUNCT atom
Similar to .ATOM, but invokes special action by fasload in case the pure
switch is on. Normally used in function calls. Briefly, if fasload is
going to purify the function it is loading, it must "snap the links" first.
If .FUNCT is used, the location will be examined by fasload and the link
snapped if possible before purification. Typical usage:
CALL 2,.FUNCT EQUAL ;calls equal as a function of 2 args
; note: the CALL is not defined
; or treated specially by MIDAS.
; (but see .FASL DEFS below)
February 7, 1977 ∪4-6.2 Page 4-49
**DRAFT** Maclisp Reference Manual **DRAFT**
.ARRAY atom
Similar to .ATOM, but assembles into a pointer to the array SAR.
.SX S-expression
Similar to .ATOM, but handles a LISP S-expression. (See below).
.SXEVA S-expression
Reads S-expression. This S-expression is evaluated (for effect
presumably) at fasload time. The resulting value is thrown away. Does not
form part of storage word.
.SXE S-expression
Similar to .SX but the S-expression is evaluated at fasload time. The
resulting value is assembled into the storage word.
The MIDAS "LISP READER"
By a conspiracy between MIDAS and fasload, a version of the LISP reader is
available. However, due to historical reasons (mostly, i.e. the fasload format
was originally intended only to deal with COMPLR type output), there are a
number of "glitches" (see below for list). These will probably tend to go away
in the fullness of time.
a) numeric ATOM
The first character of a LISP atom is examined specially. If it is a # or &,
the atom is declared to be numeric and either fixed (#) or floating (&). Midas
then proceeds to input a normal numeric field (terminated, note, by either space
or comma). This value is then "stored" in the appropriate "space" (fixnum space
or flonum space).
b) other ATOMs (also known as PNAME atoms or (LISP) SYMBOLS)
If the first character of the atom is not # or &, the atom is a "PNAME" atom.
/ becomes a single character quote character as in LISP. The atom may be
indefinitely long. The atom will be terminated by an unquoted space, carriage
return, tab, (, ), or semicolon. Unquoted linefeeds are ignored and do not
Page 4-50 ∪4-6.2 February 7, 1977
**DRAFT** Calling Programs Written in Other Languages **DRAFT**
become part of the atom. The character that terminates the atom is "used up"
unless it is a ( or ). Note that period is a legal constituent of a atom and
does not terminate it or act specially.
c) lists
Lists work normally, but note following caution relative to dot notation: .
does not terminate atoms. Thus, to invoke dot notation, the dot must be left
delimited by a space, tab, parenthesis, or other character that does terminate
atoms.
Glitches:
1) Restriction on pass dependent list structure -- In any list reading
operation, no new atoms not previously encountered may be encountered for
the first time on pass 2. However, this restriction does not apply to atom-
only reading operations (.ATOM, .SPECI, .FUNCT etc).
2) Single quote for quoting does not exist (no other macro characters exist
either.)
3) Numbers must be flagged as above always.
MOVEI A,.ATOM 123 ;LOSES - gives pointer
; to PNAME type atom
; with PNAME 123. it is
; not numeric.
use:
MOVEI A,.ATOM #123 ;wins
4) No provision exists to reference "GLOBALSYMS" in fasload. This mostly
means only that DDT must be present to load a MIDAS assembled FASL file.
(Some simple COMPLR and LAP FASL files can successfully be fasloaded by,
for example, a disowned LISP running without a DDT.)
5) LOC is illegal in a FASL assembly. BLOCK of a non-relocatable quantity is
ok.
6) Currently, symbol loading is very slow. Thus use (symbols nil), (the
initial state) unless symbols are necessary.
7) Midas does not know about any LISP symbols or UUOs specially. You should
February 7, 1977 ∪4-6.2 Page 4-51
**DRAFT** Maclisp Reference Manual **DRAFT**
`.INSRT SYS:.FASL DEFS'. This file contains definitions of symbols for all
LISP accumulators and UUOs, .GLOBAL declarations for all GLOBALSYMS, and
definitions for some internal LISP macros such as LOCKI and UNLOCKI. This
file is guaranteed to be up to date since the assembly of LISP itself uses
it.
8) .ATOM "should" be a special case of .SX . However, it is handled separately
because of the following "reasons":
a) The previously noted restriction on pass dependent LISTS.
b) Midas can do constants optimization on atoms appearing in constants (on
both pass one and pass two) but not on LISTS. Therefore, each list is
guaranteed to take a separate word in the constants area even if it is
identical to some other list which also appears in a constant.
c) Each list takes an additional entry in fasload's "atom" table. This is
a temporary table that is flushed after the fasloading is complete. Of
course, .SX still works for atoms modulo the above noted restrictions
and inefficencies.
Page 4-52 ∪4-6.2 February 7, 1977
β